﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using System.Data;
using DTO = Microsoft.MSE.Catalog.Contracts;
using System.Xml;
using System.Xml.Schema;
using System.Collections;
using System.IO;

namespace Microsoft.MSE.Catalog.Module.Data
{
    public class Schema 
    {
        public Schema()
        {

        }

        public static Guid CreateSchemaNamespace(DTO.Schema schema, SqlTransaction txn, SqlConnection conn)
        {
            bool autoclose = false;
            const string sprocname = "[ServiceEngine].[CreateSchema]";
            try
            {
                if (conn == null) conn = CatalogSvcHelper.GetConnection();
                if (conn.State == ConnectionState.Closed)
                {
                    autoclose = true;
                    conn.Open();
                }
                using (SqlCommand cmd = new SqlCommand(sprocname, conn, txn))
                {
                    SqlParameter IDParam = new SqlParameter("@ID", SqlDbType.UniqueIdentifier);
                    SqlParameter NameParam = new SqlParameter("@Name", SqlDbType.NVarChar, 400);
                    SqlParameter DescriptionParam = new SqlParameter("@Description", SqlDbType.NText);
                    SqlParameter AttributeFormDefaultParam = new SqlParameter("@AttributeFormDefault", SqlDbType.NVarChar, 32);
                    SqlParameter ElementFormDefaultParam = new SqlParameter("@ElementFormDefault", SqlDbType.NVarChar, 32);

                    cmd.CommandType = CommandType.StoredProcedure;
                    IDParam.Direction = ParameterDirection.Output;
                    NameParam.Value = schema.NamespaceURI;
                    DescriptionParam.Value = schema.Description;
                    AttributeFormDefaultParam.Value = schema.AttributeFormDefault.ToString();
                    ElementFormDefaultParam.Value = schema.ElementFormDefault.ToString();

                    cmd.Parameters.Add(IDParam);
                    cmd.Parameters.Add(NameParam);
                    cmd.Parameters.Add(DescriptionParam);
                    cmd.Parameters.Add(AttributeFormDefaultParam);
                    cmd.Parameters.Add(ElementFormDefaultParam);

                    cmd.ExecuteNonQuery();
                    schema.ID = (Guid)IDParam.Value;
                }
            }
            finally
            {
                if (autoclose && (conn != null)) conn.Close(); //Close connection if opened in this method
            }

            return schema.ID;
        }

        public static Guid UpdateSchema(DTO.Schema schema, SqlTransaction txn, SqlConnection conn)
        {
            bool autoclose = false;
            const string sprocname = "[ServiceEngine].[UpdateSchema]";
            try
            {
                if (conn == null) conn = CatalogSvcHelper.GetConnection();
                if (conn.State == ConnectionState.Closed)
                {
                    autoclose = true;
                    conn.Open();
                }
                using (SqlCommand cmd = new SqlCommand(sprocname, conn, txn))
                {
                    SqlParameter IDParam = new SqlParameter("@ID", SqlDbType.UniqueIdentifier);
                    SqlParameter DescriptionParam = new SqlParameter("@Description", SqlDbType.NText);
                    SqlParameter AttributeFormDefaultParam = new SqlParameter("@AttributeFormDefault", SqlDbType.NVarChar, 32);
                    SqlParameter ElementFormDefaultParam = new SqlParameter("@ElementFormDefault", SqlDbType.NVarChar, 32);

                    cmd.CommandType = CommandType.StoredProcedure;
                    IDParam.Value = schema.ID;
                    DescriptionParam.Value = schema.Description;
                    AttributeFormDefaultParam.Value = schema.AttributeFormDefault.ToString();
                    ElementFormDefaultParam.Value = schema.ElementFormDefault.ToString();

                    cmd.Parameters.Add(IDParam);
                    cmd.Parameters.Add(DescriptionParam);
                    cmd.Parameters.Add(AttributeFormDefaultParam);
                    cmd.Parameters.Add(ElementFormDefaultParam);

                    cmd.ExecuteNonQuery();
                    schema.ID = (Guid)IDParam.Value;
                }
            }
            finally
            {
                if (autoclose && (conn != null)) conn.Close(); //Close connection if opened in this method
            }

            return schema.ID;
        }

        public static void DeleteSchema(Guid ID, SqlTransaction txn, SqlConnection conn)
        {
            bool autoclose = false;
            const string sprocname = "[ServiceEngine].[DeleteSchema]";

            try
            {
                if (conn == null) conn = CatalogSvcHelper.GetConnection();
                if (conn.State == ConnectionState.Closed)
                {
                    autoclose = true;
                    conn.Open();
                }

                using (SqlCommand cmd = new SqlCommand(sprocname, conn, txn))
                {
                    SqlParameter IDParam = new SqlParameter("@ID", SqlDbType.UniqueIdentifier);

                    cmd.CommandType = CommandType.StoredProcedure;
                    IDParam.Value = ID;
                    cmd.Parameters.Add(IDParam);

                    cmd.ExecuteNonQuery();
                }

            }
            finally
            {
                if (autoclose && (conn != null)) conn.Close(); //Close connection if opened in this method
            }
        }

        public static DTO.Schema GetSchema(Guid ID, SqlTransaction txn, SqlConnection conn)
        {
            bool autoclose = false;
            DTO.Schema schema = new DTO.Schema();
            //XmlSchema xmlSchema = null;
            const string sprocname = "[ServiceEngine].[GetSchema]";

            try
            {
                if (conn == null) conn = CatalogSvcHelper.GetConnection();
                if (conn.State == ConnectionState.Closed)
                {
                    autoclose = true;
                    conn.Open();
                }

                using (SqlCommand cmd = new SqlCommand(sprocname, conn, txn))
                {
                    SqlParameter IDParam = new SqlParameter("@ID", SqlDbType.UniqueIdentifier);

                    cmd.CommandType = CommandType.StoredProcedure;
                    IDParam.Value = ID;
                    cmd.Parameters.Add(IDParam);

                    using (SqlDataReader reader = cmd.ExecuteReader())
                    {
                        if (reader.HasRows)
                        {
                            reader.Read();
                            schema.ID = reader.GetGuid(0);
                            schema.NamespaceURI = reader.GetString(1);
                            if (reader.IsDBNull(2))
                            {
                                schema.Description = null;
                            }
                            else
                            {
                                schema.Description = reader.GetString(2);
                            }
                            schema.AttributeFormDefault = (XmlSchemaForm)Enum.Parse(typeof(XmlSchemaForm),reader.GetString(3));
                            schema.ElementFormDefault = (XmlSchemaForm)Enum.Parse(typeof(XmlSchemaForm), reader.GetString(4));
                        }
                    }
                }

            }
            finally
            {
                if (autoclose && (conn != null)) conn.Close(); //Close connection if opened in this method
            }
            return schema;
        }

        public static DTO.Schema GetSchemaByNamespace(string schemaNamespace, SqlTransaction txn, SqlConnection conn)
        {
            bool autoclose = false;
            DTO.Schema schema = new DTO.Schema();

            try
            {
                if (conn == null) conn = CatalogSvcHelper.GetConnection();
                if (conn.State == ConnectionState.Closed)
                {
                    autoclose = true;
                    conn.Open();
                }

                Guid ID = Entity.GetIDFromName(schemaNamespace, DTO.EntityClass.Schema, txn, conn);
                if (CatalogSvcHelper.IsGuidNullOrEmpty(ID.ToString()) == false)
                {
                    schema = GetSchema(ID, txn, conn);
                }
                else
                {
                    throw new ApplicationException(string.Format("Schema ID is not found [{0}]", schemaNamespace));
                }

            }
            finally
            {
                if (autoclose && (conn != null)) conn.Close(); //Close connection if opened in this method
            }
            return schema;
        }

        public static List<DTO.Schema> ListSchemasForEndpoint(Guid ID, SqlTransaction txn, SqlConnection conn)
        {
            bool autoclose = false;
            List<DTO.Schema> list = new List<DTO.Schema>();
            List<Guid> schemaObjectIDs = new List<Guid>();

            const string sprocname = "[ServiceEngine].[ListSchemaObjectsForEndpoint]";

            try
            {
                if (conn == null) conn = CatalogSvcHelper.GetConnection();
                if (conn.State == ConnectionState.Closed)
                {
                    autoclose = true;
                    conn.Open();
                }

                using (SqlCommand cmd = new SqlCommand(sprocname, conn, txn))
                {
                    SqlParameter IDParam = new SqlParameter("@EndpointID", SqlDbType.UniqueIdentifier);

                    cmd.CommandType = CommandType.StoredProcedure;
                    IDParam.Value = ID;
                    cmd.Parameters.Add(IDParam);

                    using (SqlDataReader reader = cmd.ExecuteReader())
                    {
                        if (reader.HasRows)
                        {
                            HashSet<string> namespaces = new HashSet<string>();
                            while (reader.Read())
                            {
                                schemaObjectIDs.Add(reader.GetGuid(0));
                            }

                        }
                    }
                }

                if (schemaObjectIDs.Count > 0)
                {
                    Dictionary<string, XmlSchema> schemaHash = new Dictionary<string, XmlSchema>();
                    Dictionary<Guid, string> mappedTypeHash = new Dictionary<Guid, string>();

                    foreach (Guid schemaObjectID in schemaObjectIDs)
                    {
                        if (mappedTypeHash.ContainsKey(schemaObjectID) == false)
                        {
                            DTO.SchemaObject schemaObject = SchemaObject.GetSchemaObject(schemaObjectID, txn, conn);
                            SchemaObject.AddTypeToSchemaAndHash(schemaObject, schemaHash, mappedTypeHash, txn, conn);
                            SchemaObject.AppendDependencyObjectsToSchema(schemaObject, schemaHash, mappedTypeHash,txn,conn);

                        }
                    }
                }
            }
            finally
            {
                if (autoclose && (conn != null)) conn.Close(); //Close connection if opened in this method
            }

            return list;
        }

        public static List<DTO.SchemaListElement> ListSchemasByGroup(Guid entityGroupID, SqlTransaction txn, SqlConnection conn)
        {
            bool autoclose = false;
            List<DTO.SchemaListElement> list = new List<DTO.SchemaListElement>();
            const string sprocname = "[ServiceEngine].[ListEntitiesByGroup]";

            try
            {
                if (conn == null) conn = CatalogSvcHelper.GetConnection();
                if (conn.State == ConnectionState.Closed)
                {
                    autoclose = true;
                    conn.Open();
                }

                using (SqlCommand cmd = new SqlCommand(sprocname, conn, txn))
                {
                    SqlParameter IDParam = new SqlParameter("@entityGroupID", SqlDbType.UniqueIdentifier);

                    cmd.CommandType = CommandType.StoredProcedure;
                    IDParam.Value = entityGroupID;
                    cmd.Parameters.Add(IDParam);

                    using (SqlDataReader reader = cmd.ExecuteReader())
                    {
                        if (reader.HasRows)
                        {
                            list = new List<DTO.SchemaListElement>();
                            while (reader.Read())
                            {
                                DTO.SchemaListElement element = new DTO.SchemaListElement();
                                element.ID = reader.GetGuid(0);
                                element.Name = reader.GetString(1);
                                if (reader.IsDBNull(2))
                                {
                                    element.Description = null;
                                }
                                else
                                {
                                    element.Description = reader.GetString(2);
                                }

                                list.Add(element);
                            }
                        }
                    }
                }

            }
            finally
            {
                if (autoclose && (conn != null)) conn.Close(); //Close connection if opened in this method
            }

            return list;

        }

        public static DTO.SchemaList ListSchemas(SqlTransaction txn, SqlConnection conn)
        {
            bool autoclose = false;
            DTO.SchemaList list = new DTO.SchemaList();
            const string sprocname = "[ServiceEngine].[ListSchemas]";

            try
            {
                if (conn == null) conn = CatalogSvcHelper.GetConnection();
                if (conn.State == ConnectionState.Closed)
                {
                    autoclose = true;
                    conn.Open();
                }

                using (SqlCommand cmd = new SqlCommand(sprocname, conn, txn))
                {
                    cmd.CommandType = CommandType.StoredProcedure;

                    using (SqlDataReader reader = cmd.ExecuteReader())
                    {
                        if (reader.HasRows)
                        {
                            list.Schemas = new List<DTO.SchemaListElement>();
                            while (reader.Read())
                            {
                                DTO.SchemaListElement element = new DTO.SchemaListElement();
                                element.ID = reader.GetGuid(0);
                                element.Name = reader.GetString(1);
                                if (reader.IsDBNull(2))
                                {
                                    element.Description = null;
                                }
                                else
                                {
                                    element.Description = reader.GetString(2);
                                }
                                list.Schemas.Add(element);
                            }
                        }
                    }
                }
            }
            finally
            {
                if (autoclose && (conn != null)) conn.Close(); //Close connection if opened in this method
            }

            return list;
        }

        public static DTO.SchemaList ListDependencySchemas(Guid schemaID, SqlTransaction txn, SqlConnection conn)
        {
            bool autoclose = false;
            DTO.SchemaList dependencySchemaIDs = new DTO.SchemaList();
            const string sprocname = "[ServiceEngine].[ListDependentSchemas]";

            try
            {
                if (conn == null) conn = CatalogSvcHelper.GetConnection();
                if (conn.State == ConnectionState.Closed)
                {
                    autoclose = true;
                    conn.Open();
                }

                using (SqlCommand cmd = new SqlCommand(sprocname, conn, txn))
                {
                    SqlParameter IDParam = new SqlParameter("@SchemaID", SqlDbType.UniqueIdentifier);

                    cmd.CommandType = CommandType.StoredProcedure;
                    IDParam.Value = schemaID;
                    cmd.Parameters.Add(IDParam);

                    using (SqlDataReader reader = cmd.ExecuteReader())
                    {
                        if (reader.HasRows)
                        {
                            dependencySchemaIDs.Schemas = new List<DTO.SchemaListElement>();
                            while (reader.Read())
                            {
                                DTO.SchemaListElement schemaListElement = new DTO.SchemaListElement();

                                schemaListElement.ID = reader.GetGuid(0);
                                schemaListElement.Name = reader.GetString(1);
                                dependencySchemaIDs.Schemas.Add(schemaListElement);
                            }
                        }
                    }
                }
            }
            finally
            {
                if (autoclose && (conn != null)) conn.Close(); //Close connection if opened in this method
            }

            return dependencySchemaIDs;
        }

        public static DTO.SchemaList ListReferencedBySchemas(Guid schemaID, SqlTransaction txn, SqlConnection conn)
        {
            bool autoclose = false;
            DTO.SchemaList dependencySchemaIDs = new DTO.SchemaList();
            const string sprocname = "[ServiceEngine].[ListReferencedBySchemas]";

            try
            {
                if (conn == null) conn = CatalogSvcHelper.GetConnection();
                if (conn.State == ConnectionState.Closed)
                {
                    autoclose = true;
                    conn.Open();
                }

                using (SqlCommand cmd = new SqlCommand(sprocname, conn, txn))
                {
                    SqlParameter IDParam = new SqlParameter("@SchemaID", SqlDbType.UniqueIdentifier);

                    cmd.CommandType = CommandType.StoredProcedure;
                    IDParam.Value = schemaID;
                    cmd.Parameters.Add(IDParam);

                    using (SqlDataReader reader = cmd.ExecuteReader())
                    {
                        if (reader.HasRows)
                        {
                            dependencySchemaIDs.Schemas = new List<DTO.SchemaListElement>();
                            while (reader.Read())
                            {
                                DTO.SchemaListElement schemaListElement = new DTO.SchemaListElement();

                                schemaListElement.ID = reader.GetGuid(0);
                                schemaListElement.Name = reader.GetString(1);
                                dependencySchemaIDs.Schemas.Add(schemaListElement);
                            }
                        }
                    }
                }
            }
            finally
            {
                if (autoclose && (conn != null)) conn.Close(); //Close connection if opened in this method
            }

            return dependencySchemaIDs;
        }

        public static XmlSchema AppendImportsToSchema(DTO.Schema schema, XmlSchema xmlSchema, SqlTransaction txn, SqlConnection conn)
        {
            bool autoclose = false;
            const string sprocname = "[ServiceEngine].[ListSchemaImports]";

            try
            {
                if (conn == null) conn = CatalogSvcHelper.GetConnection();
                if (conn.State == ConnectionState.Closed)
                {
                    autoclose = true;
                    conn.Open();
                }

                using (SqlCommand cmd = new SqlCommand(sprocname, conn, txn))
                {
                    SqlParameter IDParam = new SqlParameter("@SchemaID", SqlDbType.UniqueIdentifier);

                    cmd.CommandType = CommandType.StoredProcedure;
                    IDParam.Value = schema.ID;
                    cmd.Parameters.Add(IDParam);

                    schema.ImportedSchemas = new List<DTO.SchemaImport>();
                    using (SqlDataReader reader = cmd.ExecuteReader())
                    {
                        if (reader.HasRows)
                        {
                            while (reader.Read())
                            {
                                DTO.SchemaImport si = new DTO.SchemaImport();
                                si.NamespaceURI = reader.GetString(0);
                                si.ID = reader.GetGuid(1);
                                schema.ImportedSchemas.Add(si);
                            }
                        }
                    }
                }
            }
            finally
            {
                if (autoclose && (conn != null)) conn.Close(); //Close connection if opened in this method
            }


            if (schema.ImportedSchemas.Count > 0)
            {
                XmlDocument schemaDoc = CatalogSvcHelper.ConverXmlSchemaToDOM(xmlSchema);
                foreach (DTO.SchemaImport si in schema.ImportedSchemas)
                {
                    XmlElement xeImport = schemaDoc.CreateElement(schemaDoc.DocumentElement.Prefix, "import", schemaDoc.DocumentElement.NamespaceURI);
                    XmlAttribute xaNamespace = xeImport.Attributes.Append(schemaDoc.CreateAttribute("namespace"));
                    xaNamespace.Value = si.NamespaceURI;
                    schemaDoc.DocumentElement.AppendChild(xeImport);
                }
                xmlSchema = CatalogSvcHelper.LoadSchemaFromString(schemaDoc.OuterXml);
            }
            return xmlSchema;
        }

        public static XmlSchema AppendObjectsToSchema(DTO.Schema schema, XmlSchema xmlSchema, SqlTransaction txn, SqlConnection conn)
        {
            schema.Objects = Data.SchemaObject.ListSchemaObjectForSchema(schema.ID, txn, conn);

            if (schema.Objects.Count > 0)
            {
                foreach (DTO.SchemaObject so in schema.Objects)
                {
                    XmlSchemaObject xso = CatalogSvcHelper.DeSerializeSchemaObject(so.Xml.OuterXml, new XmlQualifiedName(so.Name, schema.NamespaceURI));
                    xmlSchema.Items.Add(xso);
                }
            }
            return xmlSchema;
        }
    }
}
